package ai.people.platform.archives.service.impl;

import ai.people.core.feign.api.elastic.ElasticsearchClient;
import ai.people.core.security.entity.AuthenticationUser;
import ai.people.core.security.support.RequestHolderUtil;
import ai.people.netmon.framework.domain.archives.entity.*;
import ai.people.netmon.framework.domain.archives.request.ArchivesRequest;
import ai.people.netmon.framework.domain.archives.request.NetworkActivitiesRequest;
import ai.people.netmon.framework.domain.archives.vo.*;
import ai.people.netmon.framework.domain.searchmsg.request.MessageRequest;
import ai.people.netmon.framework.exception.enums.CommonEnum;
import ai.people.netmon.framework.model.response.PageResponse;
import ai.people.netmon.framework.model.response.Result;
import ai.people.netmon.framework.utils.AssertUtils;
import ai.people.netmon.utils.NumeralPool;
import ai.people.netmon.utils.StringPool;
import ai.people.platform.archives.service.*;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * 档案业务服务类
 *
 * @author zhenglin
 * @since 2022/3/23
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ArchivesServiceImpl implements ArchivesService {

    /**
     * 基础信息服务
     */
    @Autowired
    private ArchivesBaseInfoService baseInfoService;
    /**
     * 公司信息服务
     */
    @Autowired
    private ArchivesCompanyInfoService companyInfoService;
    /**
     * 社会信息服务
     */
    @Autowired
    private ArchivesSocialInfoService socialInfoService;
    /**
     * 协会服务
     */
    @Autowired
    private ArchivesUserAssociationService associationService;

    /**
     * 搜索客户
     */
    @Autowired
    private ElasticsearchClient searchClient;

    /**
     * 数据源
     */
    private final DataSource dataSource;

    @Autowired
    ExecutorService executorService;

    /**
     * 添加档案
     *
     * @param archives 档案
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String addArchives(ArchivesAddVO archives) throws InterruptedException, ExecutionException {
        //获取登录用户ID
        AuthenticationUser loginUser = RequestHolderUtil.loginUser();
        //保存基本信息
        ArchivesBaseInfo baseInfo = new ArchivesBaseInfo(archives);
        Long userId = loginUser.getUserId();
        baseInfo.setCreatorId(userId);
        baseInfo.setCreatorName(loginUser.getUsername());
        boolean saveBaseInfo = baseInfoService.save(baseInfo);
        AssertUtils.isTrue(saveBaseInfo, CommonEnum.FAIL);
        Long baseInfoId = baseInfo.getId();
        ConnectionHolder connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        assert Objects.nonNull(connectionHolder);
        Future<Boolean> saveCompanyResult = executorService.submit(() -> {
            try {
                // 将此异步线程绑定到主线程数据库连接 共用同一个Connection
                TransactionSynchronizationManager.bindResource(dataSource, connectionHolder);
                //保存公司信息
                return Optional.ofNullable(archives.getCompanyInfo()).map(companyInfo -> companyInfoService.save(new ArchivesCompanyInfo(companyInfo, baseInfoId)))
                        .orElseGet(() -> companyInfoService.save(new ArchivesCompanyInfo(baseInfoId)));
            } finally {
                TransactionSynchronizationManager.unbindResource(dataSource);
            }
        });

        Future<Boolean> saveSocialInfoResult = executorService.submit(() -> {
            try {
                // 将此异步线程绑定到主线程数据库连接 共用同一个Connection
                TransactionSynchronizationManager.bindResource(dataSource, connectionHolder);
                //保存社交媒体
                return Optional.ofNullable(archives.getSocialInfo()).map(socialInfo -> socialInfoService.save(new ArchivesSocialInfo(socialInfo, baseInfoId)))
                        .orElseGet(() -> socialInfoService.save(new ArchivesSocialInfo(baseInfoId)));
            } finally {
                TransactionSynchronizationManager.unbindResource(dataSource);
            }
        });

        Future<Boolean> saveAssociationResult = executorService.submit(() -> {
            try {
                // 将此异步线程绑定到主线程数据库连接 共用同一个Connection
                TransactionSynchronizationManager.bindResource(dataSource, connectionHolder);
                //保存用户和档案关系
                return associationService.save(new ArchivesUserAssociation(baseInfoId, userId));
            } finally {
                TransactionSynchronizationManager.unbindResource(dataSource);
            }
        });
        AssertUtils.isTrue(saveCompanyResult.get() && saveAssociationResult.get() && saveSocialInfoResult.get(), CommonEnum.FAIL);
        return baseInfoId.toString();
    }

    /**
     * 更新档案
     *
     * @param archives 档案
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateArchives(ArchivesVO archives) throws InterruptedException, ExecutionException {
        Long baseInfoId = archives.getId();
        ConnectionHolder connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        assert Objects.nonNull(connectionHolder);
        Future<Boolean> saveBaseInfoResult = executorService.submit(() -> {
            try {
                TransactionSynchronizationManager.bindResource(dataSource, connectionHolder);
                //更新基本信息到数据库
                ArchivesBaseInfo baseInfo = new ArchivesBaseInfo(archives);
                baseInfo.setUpdateTime(LocalDateTime.now());
                return baseInfoService.updateById(baseInfo);
            } finally {
                TransactionSynchronizationManager.unbindResource(dataSource);
            }
        });

        Future<Boolean> saveCompanyInfoResult = executorService.submit(() -> {
            try {
                TransactionSynchronizationManager.bindResource(dataSource, connectionHolder);
                //更新公司信息到数据库
                return Optional.ofNullable(archives.getCompanyInfo()).map(x -> {
                    ArchivesCompanyInfo companyInfo = new ArchivesCompanyInfo(x, baseInfoId);
                    LambdaUpdateWrapper<ArchivesCompanyInfo> companyInfoLambdaUpdateWrapper = Wrappers.lambdaUpdate();
                    companyInfoLambdaUpdateWrapper.eq(ArchivesCompanyInfo::getBaseInfoId, baseInfoId);
                    return companyInfoService.update(companyInfo, companyInfoLambdaUpdateWrapper);
                }).orElse(true);
            } finally {
                TransactionSynchronizationManager.unbindResource(dataSource);
            }
        });

        Future<Boolean> saveSocialInfoResult = executorService.submit(() -> {
            try {
                TransactionSynchronizationManager.bindResource(dataSource, connectionHolder);
                //更新社交媒体到数据库
                return Optional.ofNullable(archives.getSocialInfo()).map(x -> {
                    ArchivesSocialInfo socialInfo = new ArchivesSocialInfo(x, baseInfoId);
                    LambdaUpdateWrapper<ArchivesSocialInfo> socialInfoLambdaUpdateWrapper = Wrappers.lambdaUpdate();
                    socialInfoLambdaUpdateWrapper.eq(ArchivesSocialInfo::getBaseInfoId, baseInfoId);
                    return socialInfoService.update(socialInfo, socialInfoLambdaUpdateWrapper);
                }).orElse(true);
            } finally {
                TransactionSynchronizationManager.unbindResource(dataSource);
            }
        });

        AssertUtils.isTrue(saveBaseInfoResult.get() && saveCompanyInfoResult.get() && saveSocialInfoResult.get(), CommonEnum.FAIL);
    }

    /**
     * 档案列表
     *
     * @param request 请求
     * @return {@link PageResponse}<{@link ArchivesListVO}>
     */
    @Override
    public PageResponse<ArchivesListVO> listArchives(ArchivesRequest request) {
        Page<ArchivesBaseInfo> page = new Page<>(request.getOriginPageNo(), request.getPageSize());
        Page<ArchivesBaseInfo> result = baseInfoService.pageCustom(page, request);
        List<ArchivesBaseInfo> records = result.getRecords();
        if (records.isEmpty()) {
            return PageResponse.builderEmpty();
        }
        List<ArchivesListVO> listVOList = this.combinedArchivesList(records);
        return PageResponse.builder(listVOList, result.getTotal(), result.getPages(), result.getCurrent());
    }


    /**
     * 结合档案列表
     *
     * @param records 记录
     * @return {@link List}<{@link ArchivesListVO}>
     */
    private List<ArchivesListVO> combinedArchivesList(List<ArchivesBaseInfo> records) {
        List<ArchivesListVO> listVOList = new ArrayList<>();
        for (ArchivesBaseInfo record : records) {
            listVOList.add(new ArchivesListVO(record));
        }
        return listVOList;
    }

    /**
     * 我档案列表
     *
     * @param request 请求
     * @return {@link PageResponse}<{@link ArchivesListVO}>
     */
    @Override
    public PageResponse<ArchivesListVO> myListArchives(ArchivesRequest request) {
        Long userId = RequestHolderUtil.getUserId();
        long archivesCount = baseInfoService.myArchivesCount(request, userId);
        if (archivesCount == 0L) {
            return PageResponse.builderEmpty();
        }
        List<ArchivesBaseInfo> myArchivesPage = baseInfoService.myArchivesPage(request, userId);
        return PageResponse.builder(this.combinedArchivesList(myArchivesPage), archivesCount);
    }


    /**
     * 获取档案
     *
     * @param id id
     * @return {@link ArchivesDetailVO}
     */
    @Override
    public ArchivesDetailVO getArchives(Long id) {
        ArchivesBaseInfo baseInfo = baseInfoService.getById(id);
        AssertUtils.isNotNull(baseInfo, CommonEnum.QUERY_NOT_FOUND);
        Long baseInfoId = baseInfo.getId();
        ArchivesDetailVO archivesDetailVO = new ArchivesDetailVO(baseInfo, false);
        archivesDetailVO.companyInfo(companyInfoService.selectByBaseInfoId(baseInfoId));
        archivesDetailVO.socialInfo(socialInfoService.selectByBaseInfoId(baseInfoId));
        return archivesDetailVO;
    }

    /**
     * 网络活动列表
     *
     * @param networkActivitiesRequest 请求
     * @return {@link PageResponse}<{@link NetworkActivitiesVO}>
     */
    @Override
    public PageResponse<NetworkActivitiesVO> listNetworkActivities(NetworkActivitiesRequest networkActivitiesRequest) {
        Long archivesId = Long.valueOf(networkActivitiesRequest.getArchivesId());
        LambdaQueryWrapper<ArchivesSocialInfo> socialInfoWrapper = Wrappers.lambdaQuery();
        socialInfoWrapper.select(ArchivesSocialInfo::getSocialInfo);
        socialInfoWrapper.eq(ArchivesSocialInfo::getBaseInfoId, archivesId);
        ArchivesSocialInfo socialInfos = socialInfoService.getOne(socialInfoWrapper);
        if (Objects.isNull(socialInfos) || CollectionUtils.isEmpty(socialInfos.buildSocialInfo())) {
            return PageResponse.builder(Collections.emptyList(), 0, 0, networkActivitiesRequest.getOriginPageNo());
        }
        List<ArchivesVO.SocialInfo.SocialInfoInner> socialInfoInners = socialInfos.buildSocialInfo();
        Map<String, List<ArchivesVO.SocialInfo.SocialInfoInner>> socialPlatformGroupMap = socialInfoInners.stream()
                .filter(socialInfoInner -> StringUtils.isNotBlank(socialInfoInner.getSocialPlatform()) && (StringUtils.isNotBlank(socialInfoInner.getAccountId()) || StringUtils.isNotBlank(socialInfoInner.getUsername()) || StringUtils.isNotBlank(socialInfoInner.getUrl())))
                .collect(groupingBy(ArchivesVO.SocialInfo.SocialInfoInner::getSocialPlatform));

        // 以accountId、accountName、url为key将socialInfoInners转Map、key有多个元素冲突时通过(before, after) -> before 保留前一个元素、after保留后面的元素
        Map<String, String> accountIdMap = socialInfoInners.stream().filter(socialInfoInner -> StringUtils.isNotBlank(socialInfoInner.getAccountId())).collect(Collectors.toMap(ArchivesVO.SocialInfo.SocialInfoInner::getAccountId, x -> Optional.ofNullable(x.getPortrait()).orElse(StringPool.EMPTY), (before, after) -> before));
        Map<String, String> usernameMap = socialInfoInners.stream().filter(socialInfoInner -> StringUtils.isNotBlank(socialInfoInner.getUsername())).collect(Collectors.toMap(ArchivesVO.SocialInfo.SocialInfoInner::getUsername, x -> Optional.ofNullable(x.getPortrait()).orElse(StringPool.EMPTY), (before, after) -> before));
        Map<String, String> urlMap = socialInfoInners.stream().filter(socialInfoInner -> StringUtils.isNotBlank(socialInfoInner.getUrl())).collect(Collectors.toMap(ArchivesVO.SocialInfo.SocialInfoInner::getUrl, x -> Optional.ofNullable(x.getPortrait()).orElse(StringPool.EMPTY), (before, after) -> before));

        if (socialPlatformGroupMap.isEmpty()) {
            return PageResponse.builder(Collections.emptyList(), 0, 0, networkActivitiesRequest.getOriginPageNo());
        }
        // 构造es查询参数
        MessageRequest dataQuery = this.buildEsQuery(networkActivitiesRequest, socialPlatformGroupMap);

        log.info("query es:{}", dataQuery);
        Result search = searchClient.esRetrieve2(dataQuery);
        Object data = search.getData();

        EsResult esResult = JSONObject.parseObject(JSONObject.toJSONString(data)).toJavaObject(EsResult.class);
        List<NetworkActivitiesVO> result = new ArrayList<>();
        for (EsResult.Result esResultInfo : esResult.getList()) {
            NetworkActivitiesVO networkActivitiesVO = new NetworkActivitiesVO(esResultInfo);
            String portrait = getPic(accountIdMap, usernameMap, urlMap, networkActivitiesVO);
            networkActivitiesVO.setPortrait(portrait == null ? StringPool.EMPTY : portrait);
            result.add(networkActivitiesVO);
        }
        long total = esResult.getTotal();
        Integer pageSize = networkActivitiesRequest.getPageSize();
        double pages = Math.ceil((double) total / (double) pageSize);
        long cap = (long) NumeralPool.ONE_THOUSAND * pageSize;
        if (total > cap) {
            total = cap;
        }
        return PageResponse.builder(result, total, (long) pages, networkActivitiesRequest.getOriginPageNo());
    }

    /**
     * 得到图片
     *
     * @param accountIdMap        帐户id映射
     * @param usernameMap         用户名映射
     * @param urlMap              url映射
     * @param networkActivitiesVO 网络活动签证官
     * @return {@link String}
     */
    private String getPic(Map<String, String> accountIdMap, Map<String, String> usernameMap, Map<String, String> urlMap, NetworkActivitiesVO networkActivitiesVO) {
        String accountId = networkActivitiesVO.getAccountId();
        String accountName = networkActivitiesVO.getAccountName();

        String groupAccount = networkActivitiesVO.getGroupAccount();
        String groupName = networkActivitiesVO.getGroupName();
        String url = networkActivitiesVO.getUrl();

        String portrait = null;
        portrait = accountIdMap.get(accountId);
        if (StringUtils.isBlank(portrait)) {
            portrait = accountIdMap.get(groupAccount);
        }

        if (StringUtils.isBlank(portrait)) {
            portrait = usernameMap.get(accountId);
        }

        if (StringUtils.isBlank(portrait)) {
            portrait = usernameMap.get(groupAccount);
        }

        if (StringUtils.isBlank(portrait)) {
            portrait = usernameMap.get(accountName);
        }
        if (StringUtils.isBlank(portrait)) {
            portrait = usernameMap.get(groupName);
        }

        if (StringUtils.isBlank(portrait)) {
            portrait = accountIdMap.get(accountName);
        }
        if (StringUtils.isBlank(portrait)) {
            portrait = accountIdMap.get(groupName);
        }

        if (StringUtils.isBlank(portrait)) {
            portrait = urlMap.get(url);
        }
        return portrait;
    }


    /**
     * 得到详细档案
     *
     * @param id id
     * @return {@link ArchivesDetail2VO}
     */
    @Override
    public ArchivesDetail2VO getArchivesDetail(Long id) {
        ArchivesBaseInfo baseInfo = baseInfoService.getById(id);
        AssertUtils.isNotNull(baseInfo, CommonEnum.QUERY_NOT_FOUND);
        Long baseInfoId = baseInfo.getId();
        ArchivesDetail2VO archivesDetailVO = new ArchivesDetail2VO(baseInfo);
        archivesDetailVO.companyInfo(companyInfoService.selectByBaseInfoId(baseInfoId));
        archivesDetailVO.socialInfo(socialInfoService.selectByBaseInfoId(baseInfoId));
        return archivesDetailVO;
    }

    /**
     * 得到隐藏档案
     *
     * @param id id
     * @return {@link ArchivesDetailVO}
     */
    @Override
    public ArchivesDetailVO getHideArchives(Long id) {
        ArchivesBaseInfo baseInfo = baseInfoService.getById(id);
        AssertUtils.isNotNull(baseInfo, CommonEnum.QUERY_NOT_FOUND);
        Long baseInfoId = baseInfo.getId();
        ArchivesDetailVO archivesDetailVO = new ArchivesDetailVO(baseInfo, true);
        archivesDetailVO.companyInfo(companyInfoService.selectByBaseInfoId(baseInfoId));
        archivesDetailVO.socialInfo(socialInfoService.selectByBaseInfoId(baseInfoId));
        return archivesDetailVO;
    }

    /**
     * 检查档案存在
     *
     * @param archivesId 档案id
     * @return boolean
     */
    @Override
    public boolean checkArchivesIsExists(Long archivesId) {
        return Objects.nonNull(baseInfoService.getById(archivesId));
    }

    /**
     * 档案列表(团伙库)
     *
     * @return {@link PageResponse}<{@link GangGroupArchivesVO}>
     */
    @Override
    public List<GangGroupArchivesVO> listForGangGroup() {
        List<GangGroupArchivesBO> result = baseInfoService.selectGangGroup();
        List<GangGroupArchivesVO> list = new ArrayList<>();
        for (GangGroupArchivesBO bo : result) {
            list.add(new GangGroupArchivesVO(bo));
        }
        return list;
    }

    /**
     * 档案详情(团伙库)
     *
     * @param id id
     * @return {@link GangGroupArchivesVO}
     */
    @Override
    public GangGroupArchivesVO getForGangGroup(Long id) {
        GangGroupArchivesBO bo = baseInfoService.getForGangGroup(id);
        return new GangGroupArchivesVO(bo);
    }


    /**
     * 构建es查询
     *
     * @param networkActivitiesRequest 请求信息
     * @param socialPlatformGroupMap   社交平台分组信息
     * @return {@link MessageRequest}
     */
    private MessageRequest buildEsQuery(NetworkActivitiesRequest networkActivitiesRequest, Map<String, List<ArchivesVO.SocialInfo.SocialInfoInner>> socialPlatformGroupMap) {
        //根据关键词模糊搜索（姓名+关发言内容） 条件：该账号的配置的平台和账号（账号名称或者账号ID）
        MessageRequest dataQuery = new MessageRequest();

        Set<String> sources = socialPlatformGroupMap.keySet();
        Set<String> authorIds = new HashSet<>();
        Set<String> authorNames = new HashSet<>();
        Set<String> groupIds = new HashSet<>();
        Set<String> groupNames = new HashSet<>();
        Set<String> urls = new HashSet<>();

        socialPlatformGroupMap.forEach((k, v) -> {
            for (ArchivesVO.SocialInfo.SocialInfoInner socialInfoInner : v) {
                String accountId = socialInfoInner.getAccountId();
                String username = socialInfoInner.getUsername();
                String url = socialInfoInner.getUrl();
                if (StringUtils.isNotBlank(accountId)) {
                    authorIds.add(accountId);
                }

                if (StringUtils.isNotBlank(username)) {
                    authorNames.add(username);
                    authorIds.add(username);
                }
                urls.add(url);
            }
        });

        String searchKeywords = networkActivitiesRequest.getSearchKeywords();
        if (StringUtils.isNotBlank(searchKeywords)) {
            dataQuery.setSearchKeywords(searchKeywords);
            dataQuery.setFields("user_name,content");
        }

        if (CollectionUtils.isNotEmpty(sources)) {
            dataQuery.setSource(Strings.join(sources, ','));
        }
        if (CollectionUtils.isNotEmpty(authorIds)) {
            dataQuery.setAccountId(Strings.join(authorIds, ','));
        }
        if (CollectionUtils.isNotEmpty(authorNames)) {
            dataQuery.setAccountName(Strings.join(authorNames, ','));
        }

        if (CollectionUtils.isNotEmpty(groupIds)) {
            dataQuery.setGroupId(Strings.join(groupIds, ','));
        }
        if (CollectionUtils.isNotEmpty(groupNames)) {
            dataQuery.setGroupName(Strings.join(groupNames, ','));
        }

        if (CollectionUtils.isNotEmpty(urls)) {
            dataQuery.setUrl(Strings.join(urls, ','));
        }

        Integer pageNo = networkActivitiesRequest.getPageNo();
        Integer pageSize = networkActivitiesRequest.getPageSize();

        dataQuery.setLimit(pageSize);
        dataQuery.setOffset(pageNo);
        return dataQuery;
    }


}
